#ifndef	__ASSEMBLER__
#define	__ASSEMBLER__
#endif

#include <inc/memlayout.h>
#include <inc/sysconf.h>

#include <inc/mmu.h>
#include <inc/pic.h>
#include <kernel/kernel.h>
			  
#define	SRL(val, shamt)	(((val) >> (shamt)) & ~(-1 << (32 - (shamt))))

			  .globl delay
delay:
			  inb	$0x84, %al
			  inb	$0x84, %al
			  inb	$0x84, %al
			  inb	$0x84, %al

			  ret

			  .globl kmain_asm
kmain_asm:
			  movl	%ss:4(%esp), %eax
			  movl	%eax, %cr3
			  movl	$KSTACK_TOP, %esp
			  movl	$0, %ebp

			  call	kmain

			  //	If the kernel is over, do a rebooting
			  //	warm reboot.
			  //	change it to ``movw $0x0000, 0x472'' if you want cold rebooting

			  movw	$0x1234, 0x472
			  ljmp	$0xf000,$0xfff0


			  //	inital jump to user mode
			  .globl jump_user
jump_user:
			  cli

			  movl	$GD_USER_DATA, %eax
			  orw	$3, %ax
			  movw	%ax, %ds
			  movw	%ax, %es
			  movw	%ax, %fs
			  movw	%ax, %gs

			  //	use the uxstack
			  movl	$USTACK_TOP, %ebx
			  pushl	%eax
			  pushl	%ebx

			  // TODO - CLEARING WORK
			  //	pushf
			  //	popl	%eax
			  //	enable interrupt after leaping
			  //	orl	$FL_IF, %eax
			  //	this is the inital veflags
			  //	movl	%eax, veflags
			  //	pushl	%eax

			  pushl VEFLAGS

			  movl	$GD_USER_TEXT, %eax
			  orw	$3, %ax
			  pushl	%eax
			  pushl	%edx
			  iret

			  //	设置系统回调的返回点
			  .macro set_kcb_ret_point
			  pushl	%ebp
			  movl	%esp, %ebp

			  pushl	%ds
			  pushl	%es
			  pusha
			  pushf

			  pushl	(task + TS_ESP0_OFFSET)
			  movl	%esp, (task + TS_ESP0_OFFSET)
			  .endm

			  //	返回值
			  .macro do_kcb_ret rv

			  movl	(task + TS_ESP0_OFFSET), %esp
			  popl	(task + TS_ESP0_OFFSET)

			  popf

			  movl	\rv, %ss:28(%esp)
			  popa

			  //	xchg  %bx, %bx

			  popl	%es
			  popl	%ds
			  popl	%ebp
			  ret
			  .endm

			  //	(entry, stack)
			  .globl jump_user_pfhandler
			  
jump_user_pfhandler:
			  set_kcb_ret_point

			  movl	%ss:8(%ebp), %ebx
			  movl	%ss:12(%ebp), %edx

			  movl	$GD_USER_DATA, %eax
			  orw	$3, %ax
			  movw	%ax, %ds
			  movw	%ax, %es

			  pushl	%eax
			  pushl	%edx

			  pushl	VEFLAGS

			  movl	$GD_USER_TEXT, %eax
			  orw	$3, %ax
			  pushl	%eax
			  pushl	%ebx

			  iret

			  .globl kcb_ret
kcb_ret:
			  do_kcb_ret %eax

#if	0
			  //	(cr3)
			  //	change vpt without change the mapping of kernel and user stack
			  .globl change_cr3
change_cr3:
			  movl	%ss:4(%esp), %eax
			  movl	ks_pde, %ebx
			  movl	us_pde, %edx

			  movl	%eax, %cr3
			  movl	%ebx, ks_pde
			  movl	%edx, us_pde
			  //	refresh it
			  movl	%eax, %cr3

			  ret

			  //	(cr3, entry, esp, failed)
			  .globl call_rpc_asm
call_rpc_asm:
			  set_kcb_ret_point

			  //	cr3
			  movl	%ss:8(%ebp), %eax
			  movl	ks_pde, %ebx
			  movl	us_pde, %edx

			  movl	%eax, %cr3
			  movl	%ebx, ks_pde
			  movl	%edx, us_pde
			  //	refresh it
			  movl	%eax, %cr3

			  //	entry
			  movl	%ss:12(%ebp), %edx
			  movl	(%edx), %ebx
			  movl	vid, %eax
			  xorl	4(%edx), %eax

			  cmpl	%ebx, %eax

			  je	rpc_ok

			  //	check failed

			  //	Set failed
			  movl	%ss:20(%ebp), %eax
			  movl	$1, (%eax)
			  do_kcb_ret $0

			  //	checked
rpc_ok:
			  movl	%ss:20(%ebp), %eax
			  movl	$0, (%eax)

			  //	esp
			  movl	%ss:16(%ebp), %edx

			  movl	$GD_USER_DATA, %eax
			  orw	$3, %ax
			  movw	%ax, %ds
			  movw	%ax, %es

			  pushl	%eax
			  pushl	%edx

			  pushl	VEFLAGS

			  movl	$GD_USER_TEXT, %eax
			  orw	$3, %ax
			  pushl	%eax
			  pushl	%ebx

			  iret
#endif 

			  //	生成处理中断和异常的代码

			  //	不产生错误代码的异常
			  .macro isr_noerr n

			  .globl isr\n
isr\n:

			  pushl	$0
			  pushl	$\n


			  jmp	isr_common_handler

			  .endm

			  //	产生错误代码的异常
			  .macro isr_err n

			  .globl isr\n
isr\n:
			  pushl	$\n

			  jmp	isr_common_handler

			  .endm

			  //	直接调用 IRQ0

			  .globl call_irq0
call_irq0:
			  int	$PICV_OFFSET1
			  ret

			  //	直接调用 INT_WAIT
			  .globl call_yield
call_yield:
			  int	$INT_YIELD
			  ret


			  //	IRQ 处理程序
			  .macro irq i
			  .globl irq\i
irq\i:
			  pushl	$0
			  pushl	$\i
			  jmp	irq_common_handler

			  .endm

			  //	产生对应的实际代码

			  isr_noerr	0
			  isr_noerr	1
			  isr_noerr	2
			  isr_noerr	3
			  isr_noerr	4
			  isr_noerr	5
			  isr_noerr	6
			  isr_noerr	7
			  isr_err 8
			  isr_noerr	9
			  isr_err 10
			  isr_err 11
			  isr_err 12
			  isr_err 13
			  isr_err 14
			  isr_noerr	15
			  isr_noerr	16
			  isr_noerr	17
			  isr_noerr	18
			  isr_noerr	19
			  isr_noerr	20
			  isr_noerr	21
			  isr_noerr	22
			  isr_noerr	23
			  isr_noerr	24
			  isr_noerr	25
			  isr_noerr	26
			  isr_noerr	27
			  isr_noerr	28
			  isr_noerr	29
			  isr_noerr	30
			  isr_noerr	31

			  .macro switching_trigger name

			  pushl	%ds
			  pushl	%es
			  pusha

			  movl	%esp, %eax
			  pushl	%eax

			  movl	$0, SI_CR3
			  //	call the real handler

			  call	st_handler

			  popl	%eax
			  movl	%eax, %esp

			  //	todo switch page dir table the page table entry for tvpt
			  movl	SI_CR3, %eax
			  cmpl	$0, %eax

			  je	st_not_switch_\name

			  movl	%eax, %cr3

st_not_switch_\name:
			  //	after the switching
			  //	may handle the signal
			  call	after_switching_hook

			  popa

			  //	xchg  %bx, %bx

			  popl	%es
			  popl	%ds

			  .endm
			  
			  //	irq 0 the trigger
irq0:

			  pushl	$0
			  pushl	$0

			  switching_trigger irq0

			  //	clear the pushed err no(empty) and irq number
			  addl	$8, %esp
			  iret

			  //	int_yield the trigger
			  .global int_yield
int_yield:

			  pushl	$0
			  pushl	$INT_YIELD

			  switching_trigger	int_yield

			  //	clear the pushed err no(empty) and irq number
			  addl	$8, %esp
			  iret


			  irq	1
			  irq	2
			  irq	3
			  irq	4
			  irq	5
			  irq	6
			  irq	7
			  irq	8
			  irq	9
			  irq	10
			  irq	11
			  irq	12
			  irq	13
			  irq	14
			  irq	15

isr_common_handler:

			  pushl	%ds
			  pushl	%es
			  pusha

			  //	call the real handler

			  call	kexception

			  //	clear the pushed err no and isr number

			  popa
			  //	xchg  %bx, %bx
			  popl	%es
			  popl	%ds

			  addl	$8, %esp
			  iret

irq_common_handler:

			  pushl	%ds
			  pushl	%es
			  pusha

			  //	call the real handler
			  call	kirq

			  //	clear the pushed err no(empty) and irq number
			  popa
			  //	xchg  %bx, %bx
			  popl	%es
			  popl	%ds

			  addl	$8, %esp
			  iret

			  .globl syscall
syscall:
			  pushl	$0x0
			  pushl	%eax
			  pushl	%ds
			  pushl	%es
			  pusha

			  call	syscall_handler

			  popa
			  //	xchg  %bx, %bx

			  popl	%es
			  popl	%ds

			  addl	$8, %esp
			  iret

			  .data

			  .globl isr
isr:
			  .long	isr0
			  .long	isr1
			  .long	isr2
			  .long	isr3
			  .long	isr4
			  .long	isr5
			  .long	isr6
			  .long	isr7
			  .long	isr8
			  .long	isr9
			  .long	isr10
			  .long	isr11
			  .long	isr12
			  .long	isr13
			  .long	isr14
			  .long	isr15
			  .long	isr16
			  .long	isr17
			  .long	isr18
			  .long	isr19
			  .long	isr20
			  .long	isr21
			  .long	isr22
			  .long	isr23
			  .long	isr24
			  .long	isr25
			  .long	isr26
			  .long	isr27
			  .long	isr28
			  .long	isr29
			  .long	isr30
			  .long	isr31

			  .globl irq
irq:
			  .long	irq0
			  .long	irq1
			  .long	irq2
			  .long	irq3
			  .long	irq4
			  .long	irq5
			  .long	irq6
			  .long	irq7
			  .long	irq8
			  .long	irq9
			  .long	irq10
			  .long	irq11
			  .long	irq12
			  .long	irq13
			  .long	irq14
			  .long	irq15

			  .globl vpt
			  .set	vpt, KVPT
			  .globl vpd
			  .set	vpd, (KVPT + SRL(KVPT, 10))
			  
			  .globl kstack_alloc
			  .set kstack_alloc, KSTACK_ALLOC

			  .globl dyninfo
			  .set dyninfo, DYNINFO

			  .align PAGE_SIZE
			  .globl klog_pipe
			  // 系统信息管道所用的空间
klog_pipe:
			  .space PAGE_SIZE * 2
			  .align 1

			  //	include the user lib code here
			  .align 1
			  .globl user_prog
			  .globl user_prog_end


user_prog:
#include "userprog.S"
user_prog_end:
